#ifndef INC_TWEAKERTOOL_H
#define INC_TWEAKERTOOL_H

#include "Tool.h"

class UMLObject;

class TweakerTool : public Tool
{
	public:
		TweakerTool(DiagramMan&);

		/*** MouseListener functions ***/
		bool mouseMoveEvent(QMouseEvent&);
		bool mousePressEvent(QMouseEvent&);
		bool mouseReleaseEvent(QMouseEvent&);

	private:

		// ABC for State pattern
		class State
		{
			public:
				State(TweakerTool* context);

				/*** ACCESSORS ***/

				TweakerTool& context();

				/*** STATE PATTERN INTEFRACE ***/

				virtual bool mouseMoveEvent(QMouseEvent&) = 0;
				virtual bool mousePressEvent(QMouseEvent&) = 0;
				virtual bool mouseReleaseEvent(QMouseEvent&) = 0;

			private:
				TweakerTool* _context; // init-checked
		};

		/**
		 * Default meaning we're pretty much just waiting for the user to do
		 * something.
		 *
		 * - mouseMove: nothing (transition to MovingState by ReselectState)
		 * - mousePress: selects first item under cursor
		 * - mouseRelease: nothing
		 */
		class DefaultState : public State
		{
			public:
				DefaultState(TweakerTool* context);

				void takeControl();

				bool mouseMoveEvent(QMouseEvent&);
				bool mousePressEvent(QMouseEvent&);
				bool mouseReleaseEvent(QMouseEvent&);
		} _defaultState;

		/**
		 * Like default, but for the selection-change feature.
		 *
		 * - mouseMove: switches to DefaultState or MovingState
		 * - mousePress: nothing
		 * - mouseRelease: selects next item at position
		 */
		class ReselectState : public State
		{
			public:
				ReselectState(TweakerTool* context);

				/**
				 * Takes control, provided the last object selected (so that the
				 * next selection will be accurate. */
				void takeControl(UMLObject* last);

				bool mouseMoveEvent(QMouseEvent&);
				bool mousePressEvent(QMouseEvent&);
				bool mouseReleaseEvent(QMouseEvent&);
				
			private:
				UMLObject* _last; // init-checked
		} _reselectState;

		/**
		 * User is dragging something with the mouse.
		 *
		 * - mouseMove: send transitive MoveCommand
		 * - mousePress: should never receive, state only active afterwards
		 * - mouseRelease: sends final MoveCommand
		 */
		class MovingState : public State
		{
			public:
				MovingState(TweakerTool* context);

				void takeControl(UMLObject* target, const QPoint& initial);

				bool mouseMoveEvent(QMouseEvent&);
				bool mousePressEvent(QMouseEvent&);
				bool mouseReleaseEvent(QMouseEvent&);

			private:

				void sendCommand(const QPoint& mouse, bool transitive);

				UMLObject* _target;

				// see sendCommand()
				QPoint _previous;
		} _movingState;

		// should never be NULL
		State* _state; // init-checked

		friend class DefaultState;
		friend class ReselectState;
		friend class MovingState;
};

inline TweakerTool& TweakerTool::State::context()
{
	return *_context;
}

#endif // INC_TWEAKERTOOL_H

